08 Vue 组件间通信

465次阅读
没有评论

共计 8796 个字符,预计需要花费 22 分钟才能阅读完成。

引入

组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用, 如果想要组件间通信就需要使用一些 Vue 提供给我们的方法, 下面介绍几种常见的方法

一. 父组件向子组件传值

1. 方式

  • 父组件定义 js 变量, 通过属性指令方式放置在子组件的标签上, 子组件内使用 props 来指定接收父组件传来的属性指令
// proos 指定传值的属性
props:['myname','myage'],

// 还可以以对象的方式限制传过来值的类型
props:{
    myname:String,  // 限制 myname 属性的值为字符串类型
    myage:Number,   // 限制 myage 属性传过来的值为数值
},

2. 示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
</head>
<body>
<div id="app">
    <h3> 根组件 </h3>
    <button @click="root1" class="btn btn-info"> 根组件按钮 </button>
    <br>
    <!-- 父传子 -->
    <!-- 直接通过 " 属性 = 值 " 方式传递 -->
    <child2 myname="xing" myage="100"></child2>
    <br>
    <!-- 通过属性指令传一个 js 变量, 父可在 data 中定义该变量 -->
    <child2 :myname="name" :myage="age"></child2>
    <br>
    <!-- 通过属性指令直接传字符串类型的值 -->
    <child2 :myname="'song'" :myage="88"></child2>
    <hr>
    <myhead></myhead>
    <hr>
</div>
</body>
<script>
    // 根 (父) 组件
    var vm = new Vue({
        el: '#app',
        data: {
            // 通过 js 变量给子组件进行传值
            name:'shawn',
            age:99
        },
        methods: {root1() {alert('我是根 div 的按钮!')
            }
        },
        // 根组件的局部组件, 组件名 "child2"
        components:{
            child2:{
                // 在局部组件中渲染父组件传过来的值
                template:`
                <div>
                <h4> 根下局部组件 </h4>
                    <button @click="handleClick" class="btn btn-success"> 根下的局部组件按钮:{{isShow?'隐藏':'展示'}}</button>
                    <p v-show="isShow"> 人面不知何处去🌙桃花依旧笑春风 </p>
                    <br>
                    你的名字 : {{myname}}
                    你的年龄 : {{myage}}
                </div>
                `,
                // 父传子是通过属性来传递的, 需要在子组件内使用 props 指定传值的属性
                // props:['myname','myage'],
                // 除了使用这种方式, 还可以以对象的方式限制传过来值的类型
                props:{
                    myname:String,  // 限制 myname 属性的值为字符串类型
                    myage:Number,   // 限制 myage 属性传过来的值为数值
                },
                // 局部组件内的 js 变量
                data(){
                    return{isShow: true,}
                },
                // 局部组件使用的函数
                methods:{handleClick(){this.isShow = !this.isShow}
                }
            }
        }
    })
</script>
</html>

3. 效果展示

08 Vue 组件间通信

二. 子组件向父组件传值 (通过事件的形式)

1. 方式

  • 通过 $emit 来自动触发自定义事件的执行
<!--HTML 中自定义的事件 -->
<child2 @myevent="handleParent" @custom="handleCustom"></child2>
// 使用 $emit 无论在哪里都能指定触发某个自定义事件的执行
this.$emit('myevent',this.name,this.age)  // this 指的是 Vue 实例
this.$emit('myevent')  // 也可以不传值

this.$emit('custom')

2. 示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
</head>
<body>
<div id="app">
    <h3> 根组件 </h3>
    <button @click="root1" class="btn btn-info"> 根组件按钮 </button>
    <br>
    <!-- 局部 (子) 组件, 在子组件标签内定义了两个自定义的事件, 可以通过它们来进行传值 -->
    <child2 @myevent="handleParent" @custom="handleCustom"></child2>
    <hr>
    子传父得到的值 : 你的名字 : {{name}} 你的年龄 : {{age}}
</div>
</body>
<script>
    // 根 (父) 组件
    var vm = new Vue({
        el: '#app',
        data: {// 根 (父) 组件中可以定义空的 js 变量来接收子组件传过来的值
            name:'',
            age:0,
        },
        methods: {root1() {alert('我是根 div 的按钮!')
            },
            // 根 (父) 组件中执行该函数接收两个来自子组件的参数, 并将值赋给自己的 js 变量
            handleParent(name,age){console.log('自定义事件被执行了')
                this.name = name
                this.age = age
            },
            handleCustom(){console.log('handleCustom 被自动执行了')
            }
        },
        // 根内局部 (子) 组件, 组件名 "child2"
        components:{
            child2:{
                template:`

                <div>
                <h4> 根下的局部组件 </h4>
                    <button @click="handleClick" class="btn btn-success"> 局部组件按钮(点击发送数据给根)</button>
                </div>
                `,
                // 子组件使用的 js 变量
                data(){
                    return{
                        // 子组件内定义的 js 变量, 等下传给父组件
                        name:'shawn',
                        age:990
                    }
                },
                // 子组件使用的函数
                methods:{handleClick(){console.log('子组件按钮被点击')
                        // 子传父通过 "$emit" 自动触发事件的执行来进行传值
                        // 下面是触发自定义事件 "myevent" 的执行, 并传入两个当前子组件内的 js 变量
                        this.$emit('myevent',this.name,this.age)
                        // this.$emit('myevent')  // 也可以不传值
                        // this.$emit('custom')
                    },
                },
                // 生命期钩子函数 : DOM 渲染之后触发(挂载之后)
                // "$emit" 指的是自动触发某一个指定的事件, 并不是要指定在某个位置, 可以是任意位置(渲染之后)
                mounted(){this.$emit('custom')
                }
            }
        }
    })
</script>
</html>

3. 展示

08 Vue 组件间通信

三.ref 实现父子双向通信

1. 使用方式

  • 在标签或组件上添加 ref 属性
  • 通过 this.$refs.[ref 属性值] 拿到原生节点或者组件对象, 之后再进行操作

2.ref 使用注意

  • ref 放在标签上, 拿到的是原生节点
console.log(this.$refs.myinput1) 
// <input type="text" placeholder=" 父组件 input1">
  • ref 放在组件上, 拿到的是组件对象
console.log(this.$refs.mychild)  
// a{_uid: 1, _isVue: true, $options: {…}, _renderProxy: a, _self: a,…}
// 对象中的数据、函数都可以被直接使用

3. 示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
</head>
<body>
<div id="app">
    <h3> 根组件 </h3>
    <input type="text" placeholder=" 父组件 input1" ref="myinput1"> 根组件按钮 1 </input><br>
    <input type="text" placeholder=" 父组件 input2" ref="myinput2"> 根组件按钮 2 </input><br>
    <button @click="handleParent" class="btn btn-info"> 根组件按钮 </button>
    (子传父)通过 "$refs" 得到的子组件的 name : {{name}}, age : {{age}}
    <hr>
    <myhead ref="mychild" @custom="customParent"></myhead>
</div>
</body>
<script>
    Vue.component('myhead', {
        template: `
          <div>
          <h3> 全局组件 </h3>
          <button @click="myheadButton" class="btn btn-success"> 全局 (子) 组件按钮 </button>
          <br>
          <p>(父传子)父组件传给子组件的值:mag:{{msg}}, msg2:{{msg2}}</p>
          </div>
        `,
        data() {
            return {
                // 父组件通过 ref 拿到子组件对象后可以拿到子组件的任意 js 变量和函数
                name: 'shawn',
                age: 333,
                msg: '',
                msg2: ''
            }
        },
        methods: {myheadButton() {console.log('子组件 button 被点击')
                this.$emit('custom')
            },
            // 子组件方法
            childCustom(msg2) {console.log('父组件进行了传值')
                this.msg2 = msg2
            }
        },
    })
    var vm = new Vue({
        el: '#app',
        data: {
            name: '',
            age: 0
        },
        methods: {customParent(){
                // 拿到子组件内的 name,age 赋值给自己的 name,age 实现子传父
                this.name = this.$refs.mychild.name
                this.age = this.$refs.mychild.age
            },
            handleParent() {console.log('父组件 button 被点击')

                // 拿到子组件的 msg, 并使用自己的数据对其进行赋值, 实现父传子
                this.$refs.mychild.msg = this.$refs.myinput1.value
                // 也可以触发子组件的方法, 传入自己的值作为参数
                this.$refs.mychild.childCustom(this.$refs.myinput2.value)

                // ref 放在标签上,拿到的是原生节点
                console.log(this.$refs.myinput1) // <input type="text" placeholder=" 父组件 input1">
                // ref 放在组件上,拿到的是组件对象
                console.log(this.$refs.mychild)  // a{_uid: 1, _isVue: true, $options: {…}, _renderProxy: a, _self: a,…}
            }
        },
    })
</script>
</html>

4. 展示

08 Vue 组件间通信

四. 事件总线实现不同组件间通信

1. 使用步骤

  • 创建一个事件总线实例
  • 组件可以通过事件总线发送数据(对应一个事件名)
  • 而另一个组件可以监听该事件名, 一旦有数据发过来了就会接收到
  • 可以多个组件共同监听一个事件

2. 示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
</head>
<body>
<div id="app">
    <!-- 全局组件 1 -->
    <child1></child1>
    <hr>
    <!-- 全局组件 2 -->
    <child2></child2>
</div>
</body>
<script>
    // 创建一个事件总线实例
    var bus = new Vue()
    // 创建一个全局组件 1
    Vue.component('child1', {
        template: `
            <div>
              <h4> 全局组件 1 </h4>
                <input type="text" v-model="mytext">
                <button @click="handleClick" class="btn btn-info"> 点击提交给另一个组件 </button>
            </div>
        `,
        data(){
            return{mytext:''}
        },
        methods: {handleClick() {
                // 通过事件总线发送数据,'submits' 是被另一个组件在总线上监听的名字
                bus.$emit('submits',this.mytext)
            }
        },
    })
    // 创建另一个全局组件 2
    Vue.component('child2', {
        template: `
            <div>
                <h4> 全局组件 2 </h4>
            <p> 来自全局组件 1 的数据 : {{mycontent}}</p>
            </div>
        `,
        data(){
            return{mycontent:''}
        },
        // 在挂载之后就进行监听(DOM 渲染完成后)
        mounted(){// 监听事件总线上的 "submits", 如果有数据就会接收到(观察者模式)
            bus.$on('submits',(item) =>{this.mycontent = item  // 将值赋给自己的 js 变量})
        }
    })
    // 根组件
    var vm = new Vue({
        el: '#app',
        data: {},})
</script>
</html>

3. 展示

08 Vue 组件间通信

五. 动态组件

1. 使用方式

  • vue 动态组件用于实现在指定位置上,动态加载不同的组件
<component :is="who"></component>
  • "who" 为自定义的变量,将需要加载的组件名赋值给它,即可在 <component> 标签出现的位置,渲染该组件
var vm = new Vue({
    el: '#app',
    data: {who: 'myhead1' // 赋值组件名},
})

2. 示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
</head>
<body>
<div id="app">
    <button @click="who='myhead1'"> 文章 </button>
    <button @click="who='myhead2'"> 相册 </button>
    <button @click="who='myhead3'"> 管理 </button>

    <!-- 点击哪个按钮就会切换到哪个按钮对应的组件 -->
    <component :is="who"></component>
</div>
</body>
<script>
    // 🔰文章组件
    Vue.component('myhead1', {
        template: `
            <div>
                <hr>
                标题: <br>
                <input type="text" placeholder=" 请输入标题 ">
                <br>
                内容: <br>
                <textarea cols="30" rows="10"> 请输入内容 </textarea>
                <br>
                <button class="btn btn-info"> 提交文章 </button>
            </div>
        `,
    })

    // 🔰相册组件
    Vue.component('myhead2', {
        template: `
            <div>
                <div style="height: 200px;background-color: #7de7bd"> 图片 1 </div>
                <hr>
                <div style="height: 200px;background-color: #71d058"> 图片 2 </div>
            </div>
        `,
    })

    // 🔰后台设置组件
    Vue.component('myhead3', {
        template: `
            <div>
            <hr>
                设置:<input type="text" placeholder=" 请输入设置 ">
                <br>
                设置:<input type="text" placeholder=" 请输入设置 ">
                <br>
                设置:<input type="text" placeholder=" 请输入设置 ">
                <br>
                <button class="btn btn-success"> 提交设置 </button>
            </div>
        `,
    })

    // 🔰根组件
    var vm = new Vue({
        el: '#app',
        data: {who: 'myhead1' // 默认展示这个组件},
    })
</script>
</html>

3. 展示

08 Vue 组件间通信

上面存在的一个 问题 : 只要组件切换, 之前在输入框内输入的内容就被清空了, vue 提供了 keep-alive 来帮我们保持输入框内容保持

4.keep-alive 的使用

  • 使用很简单, 只需要在 component 外套一层 keep-alive 标签即可
<!--keep-alive(保持存活)可以让输入框内有的内容一直保持, 不会因为切换而清空 -->
<keep-alive>
    <!-- 点击哪个按钮就会切换到哪个按钮对应的组件 -->
    <component :is="who"></component>
</keep-alive>

5.keep-alive 效果展示

08 Vue 组件间通信

正文完
 
shawn
版权声明:本站原创文章,由 shawn 2023-06-16发表,共计8796字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)